package dao

import javax.inject.Inject
import models.Tables._
import play.api.db.slick.{DatabaseConfigProvider, HasDatabaseConfigProvider}
import slick.jdbc.JdbcProfile
import tool.Pojo.{CircRnaData, PageData}
import tool.Tool

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global


/**
 * Created by yz on 2017/6/1.
 */
class BrowseCircRnaDao @Inject()(protected val dbConfigProvider: DatabaseConfigProvider) extends
  HasDatabaseConfigProvider[JdbcProfile] {

  import profile.api._

  type MyRow = BrowseCircRnaRow
  val table = BrowseCircRna

  def insertAll(circallpositions: Seq[MyRow]): Future[Unit] = db.run(table ++=
    circallpositions).map(_ => ())

  def deleteAll(): Future[Unit] = db.run(table.delete).map(_ => ())

  def selectByCircIds(circIds: Seq[String]) = {
    db.run(table.filter(_.circId.inSetBind(circIds)).result.map(_.toList))
  }

  def selectAllByGeneSymbol(vs: Seq[String]) = {
    db.run(table.filter(_.geneSymbol.inSetBind(vs)).result.map(_.toList))
  }

  def selectAllByGeneSymbolOrId(ids: Seq[String]) = {
    db.run(table.filter { x =>
      x.circId.inSetBind(ids) || x.geneSymbol.inSetBind(ids)
    }.result)
  }

  def selectByPosition(chr: String, start: Int, end: Int) = {
    val q = for (x <- table if x.chromosome === chr && ((x.start <= end && x.start >= start) ||
      (x.end <= end && x.end >= start) || (x.end > end && x.start < start))) yield {
      x
    }
    db.run(q.result)
  }

  def selectAll = {
    db.run(table.result)
  }

  def selectAll(ids: List[String]) = db.run(table.
    filter(_.circId.inSetBind(ids)).result.map(_.toList))

  def selectAllCircId: Future[Seq[String]] = {
    db.run(table.map(_.circId).result)
  }

  def selectAllGeneSymbol: Future[Seq[String]] = {
    db.run(table.map(_.geneSymbol).filter(_.toUpperCase =!= "NA").distinct.result)
  }

  def selectAllSampleTypes: Future[Seq[String]] = {
    db.run(table.map(_.sampleType).distinct.result)
  }

  def selectAllDiffGroup = {
    db.run(table.map(_.diffGroup).distinct.result.map(_.toList))
  }

  def selectAll(data: CircRnaData, pageData: PageData) = {
    val filterData = table.filterOpt(data.samples) { (x, ys) =>
      val allB = ys.map { y =>
        x.sampleType.like(s"%${y}%")
      }
      val b = data.samplesMethod match {
        case "union" => allB.reduceLeft((t, z) => t || z)
        case "inter" => allB.reduceLeft((t, z) => t && z)
      }
      b
    }.filterIf(data.chr != "all") { x =>
      x.chromosome === data.chr
    }.filterIf(data.circBase != "both") { x =>
      data.circBase match {
        case "exist" => x.circBaseId.toUpperCase =!= "NA"
        case "na" => x.circBaseId.toUpperCase === "NA"
      }
    }.filterIf(data.geneSymbol != "both") { x =>
      data.geneSymbol match {
        case "exist" => x.geneSymbol.toUpperCase =!= "NA"
        case "na" => x.geneSymbol.toUpperCase === "NA"
      }
    }.filterOpt(data.start) { (x, y) =>
      x.start >= y
    }.filterOpt(data.end) { (x, y) =>
      x.end <= y
    }.filterOpt(data.dfMin) { (x, y) =>
      x.detectionFrequency >= y
    }.filterOpt(data.dfMax) { (x, y) =>
      x.detectionFrequency <= y
    }.filterOpt(data.geneName) { (x, y) =>
      x.geneSymbol === y
    }.filterOpt(data.diffSamples) { (x, ys) =>
      val allB = ys.map { y =>
        x.diffGroup.like(s"%${y}%")
      }
      val b = data.diffSamplesMethod match {
        case "union" => allB.reduceLeft((t, z) => t || z)
        case "inter" => allB.reduceLeft((t, z) => t && z)
      }
      b
    }.filterOpt(pageData.search) { (x, y) =>
      val columns = List("circ_id", "circ_base_id", "genomic_position", "gene_symbol", "sample_type",
        "diff_group")
      columns.map { columnName =>
        x.getStrColumn(columnName).like(s"%${y}%")
      }.reduceLeft((x, y) => x || y)
    }
    val intColumnNames = List()
    val doubleColumnNames = List("detection_frequency", "tumor_mean", "benign_mean", "health_mean", "urine_mean",
      "csf_mean", "bile_mean")
    val sortData = data.sort match {
      case None => filterData
      case Some(x) =>
        val columnName = Tool.getTrueColumnName(x)
        val order = data.order
        columnName match {
          case x if intColumnNames.contains(columnName) => order match {
            case "asc" => filterData.sortBy { y =>
              y.getIntColumn(columnName)
            }
            case "desc" => filterData.sortBy { x =>
              x.getIntColumn(columnName).desc
            }
          }
          case x if doubleColumnNames.contains(columnName) => order match {
            case "asc" => filterData.sortBy { y =>
              y.getDoubleColumn(columnName)
            }
            case "desc" => filterData.sortBy { x =>
              x.getDoubleColumn(columnName).desc
            }
          }
          case _ => order match {
            case "asc" => filterData.sortBy { y =>
              y.getStrColumn(columnName)
            }
            case "desc" => filterData.sortBy { x =>
              x.getStrColumn(columnName).desc
            }
          }
        }
    }
    val finalData = sortData
    val countF = finalData.length.result
    val resultF = finalData.drop(pageData.offset).take(pageData.limit).result
    val f = countF.zip(resultF)
    db.run(f)
  }

  def selectDiffSamples: Future[Seq[String]] = {
    db.run(table.map(_.diffGroup).distinct.result).
      map(x => x.flatMap(_.split(",")).distinct.filter(_ != ""))
  }

}
